﻿//=============================================================================
// Project  : NIPO Component Framework
// File    : EventRegistrar.cs
// Author  : Bernhard Dieber (Bernhard.Dieber@uni-klu.ac.at)
// Copyright 2010 by Bernhard Dieber
// This code is published under the Microsoft Public License (Ms-PL).  A copy
// of the license should be distributed with the code.  It can also be found
// at the project website: http://NIPO.CodePlex.com.   This notice, the
// author's name, and all copyright notices must remain intact in all
// applications, documentation, and source files.
//=============================================================================


using System;
using System.Collections.Generic;
using NIPO.Data;
using System.Reflection;
using System.Text.RegularExpressions;
using NIPO.Plugins;
using NIPO.Util;
namespace NIPO.Controller
{
    /// <summary>
    /// This class is used for generic event registration <br />
    /// Eventhandler for various events can be registered by specifiying search parameters
    /// </summary>
    public sealed class EventRegistrar
    {
        #region Singleton
        private static readonly EventRegistrar Inst = new EventRegistrar();

        /// <summary>
        /// Initializes a new instance of the <see cref="EventRegistrar"/> class.
        /// </summary>
        private EventRegistrar()
        {
        }

        /// <summary>
        /// Returns the singleton instance of EventRegistrar
        /// </summary>
        /// <value>The instance.</value>
        public static EventRegistrar Instance
        {
            get
            {
                return Inst;
            }

        }
        #endregion

        #region Private memebers

        private List<Plugins.Plugin> _plugins = new List<Plugins.Plugin>();
        #endregion
        /// <summary>
        /// Adds a plugin to the list of possible event dispatchers <br/>
        /// This method checks if the specified plugin exposes any suitable events.
        /// </summary>
        /// <param name="plugin">The plugin to add</param>
        /// <returns><c>true</c> if successful, <c>false</c> otherwise</returns>
        public bool AddPlugin(Plugins.Plugin plugin)
        {
            if (!_plugins.Contains(plugin))
            {
                _plugins.Add(plugin);
                return true;
            }
            return false;
        }

        #region Register handler using EventName
        /// <summary>
        /// Registers a new event handler. Searches for suitable events by eventName and declaring type
        /// </summary>
        /// <param name="eventName">Regex string containing the name of an event</param>
        /// <param name="declaringType">The type declaring the intended event</param>
        /// <param name="handler">The Eventhandler</param>
        /// <returns>The number of matching events.</returns>
        public int RegisterEventHandler(string eventName, Type declaringType, NIPOEventHandler handler)
        {
            int pluginsFound = 0;
            foreach (Plugin p in _plugins)
            {
                //Check type
                if (p.GetType() != declaringType) continue;

                //Check plugin name
                pluginsFound += RegisterHandlerByName(p, eventName, handler);


            }

            return pluginsFound;
        }

        /// <summary>
        /// Registers an event handler. Searches matching events by name and declaring plugin type
        /// </summary>
        /// <param name="eventName">Name of the event.</param>
        /// <param name="pluginType">Type of the plugin.</param>
        /// <param name="handler">The handler.</param>
        /// <returns>The number of matching events</returns>
        public int RegisterEventHandler(string eventName, PlugInType pluginType, NIPOEventHandler handler)
        {
            int pluginsFound = 0;
            foreach (Plugin p in _plugins)
            {
                //Check type
                if (!p.GetType().IsSubclassOf(Plugin.GetType(pluginType))) continue;

                //Check plugin name
                pluginsFound += RegisterHandlerByName(p, eventName, handler);
            }

            return pluginsFound;
        }

        /// <summary>
        /// Registers a new event handler. Searches for suitable events by the given eventName.
        /// </summary>
        /// <param name="eventName">Name of the event.</param>
        /// <param name="handler">The handler.</param>
        /// <returns>The number of matching events</returns>
        public int RegisterEventHandler(string eventName, NIPOEventHandler handler)
        {
            int pluginsFound = 0;
            foreach (Plugin p in _plugins)
            {
                //Check plugin name
                pluginsFound += RegisterHandlerByName(p, eventName, handler);
            }

            return pluginsFound;
        }
        /// <summary>
        /// Registers an event by its name
        /// </summary>
        /// <param name="p">The plugin to find matching events in</param>
        /// <param name="eventName">The event name to search for (regular expression)</param>
        /// <param name="handler">The handler to register</param>
        /// <returns><c>1</c> if an event was found, <c>0</c> otherwise</returns>
        private int RegisterHandlerByName(Plugin p, string eventName, NIPOEventHandler handler)
        {
            Regex regex;
            try { regex = new Regex(eventName); }
            catch (Exception)
            {
                new Logger(GetType().FullName).WriteErrorFormat("Illegal Regex Expression supplied for handler {0}", handler);
                return 0;
            }
            int pluginsFound = 0;
            EventInfo[] events = p.GetType().GetEvents();
            foreach (EventInfo ev in events)
            {
                if (!regex.Match(ev.Name).Success) continue;
                
                pluginsFound++;
                ev.RemoveEventHandler(p, handler);
                ev.AddEventHandler(p, handler);
            }
            return pluginsFound;
        }
        #endregion

        #region Register Handler using Attributes
        /// <summary>
        /// Registers a new eventhandler using a given list of attributes. The <paramref name="handler"/> will be registered for every event that declares <b>all</b> attributes in <paramref name="eventAttributes"/>
        /// </summary>
        /// <param name="eventAttributes">The event attributes.</param>
        /// <param name="handler">The handler.</param>
        /// <returns>The number of matching events</returns>
        public int RegisterEventHandler(List<Attribute> eventAttributes, NIPOEventHandler handler)
        {
            if (eventAttributes == null) return 0;
            int pluginsFound = 0;
            foreach (Plugin p in _plugins)
            {
                pluginsFound += RegisteHandlerByAttributes(p, eventAttributes, handler);
            }

            return pluginsFound;

        }
        /// <summary>
        /// Registers a new eventhandler using a given list of attributes and a plugin type.<br />
        /// The <paramref name="handler"/> will be registered for every event that declares <b>all</b> attributes in <paramref name="eventAttributes"/> and is of the NIPO plugin type specified with <paramref name="pluginType"/>
        /// </summary>
        /// <param name="eventAttributes">The event attributes.</param>
        /// <param name="pluginType">The NIPO plugin type that should declare an event</param>
        /// <param name="handler">The handler.</param>
        /// <returns>The number of matching events</returns>
        public int RegisterEventHandler(List<Attribute> eventAttributes, PlugInType pluginType, NIPOEventHandler handler)
        {
            if (eventAttributes == null) return 0;
            int pluginsFound = 0;
            foreach (Plugin p in _plugins)
            {
                if (!p.GetType().IsSubclassOf(Plugin.GetType(pluginType))) continue;
                pluginsFound += RegisteHandlerByAttributes(p, eventAttributes, handler);
            }

            return pluginsFound;

        }
        /// <summary>
        /// Registers a new eventhandler using a attribute<br/>
        /// The <paramref name="handler"/> will be registered for every event that declares <paramref name="attribute"/>
        /// </summary>
        /// <param name="attribute">The event attributes.</param>
        /// <param name="handler">The handler.</param>
        /// <returns>The number of matching events</returns>
        public int RegisterEventHandler(Attribute attribute, NIPOEventHandler handler)
        {
            if (attribute == null) return 0;
            int pluginsFound = 0;
            foreach (Plugin p in _plugins)
            {
                List<Attribute> tmp = new List<Attribute> { attribute };
                pluginsFound += RegisteHandlerByAttributes(p, tmp, handler);
            }

            return pluginsFound;
        }
        /// <summary>
        /// Registers a new eventhandler using a given attribute and a plugin type.<br />
        /// The <paramref name="handler"/> will be registered for every event that declares <paramref name="attribute"/> and is of the NIPO plugin type specified with <paramref name="pluginType"/>
        /// </summary>
        /// <param name="attribute">The event attributes.</param>
        /// <param name="pluginType">The NIPO plugin type that the declarint type must be</param>
        /// <param name="handler">The handler.</param>
        /// <returns>The number of matching events</returns>
        public int RegisterEventHandler(Attribute attribute, PlugInType pluginType, NIPOEventHandler handler)
        {
            if (attribute == null) return 0;
            int pluginsFound = 0;
            foreach (Plugin p in _plugins)
            {
                if (!p.GetType().IsSubclassOf(Plugin.GetType(pluginType))) continue;
                List<Attribute> tmp = new List<Attribute> { attribute };
                pluginsFound += RegisteHandlerByAttributes(p, tmp, handler);
            }

            return pluginsFound;

        }

        /// <summary>
        /// Registes a new event handler by attributes.
        /// </summary>
        /// <param name="p">The plugin to search for matching events in.</param>
        /// <param name="eventAttributes">The event attributes. <paramref name="p"/> must declare all attributes in <paramref name="eventAttributes"/></param>
        /// <param name="handler">The handler.</param>
        /// <returns>The number of matching events</returns>
        private static int RegisteHandlerByAttributes(Plugin p, IEnumerable<Attribute> eventAttributes, NIPOEventHandler handler)
        {
            int pluginsFound = 0;
            EventInfo[] events = p.GetType().GetEvents();
            foreach (EventInfo ev in events)
            {
                List<object> atr = new List<object>(ev.GetCustomAttributes(true));
                bool containsAll = true;
                foreach (Attribute attrib in eventAttributes)
                {
                    if (!atr.Contains(attrib))
                        containsAll = false; ;

                }
                if (containsAll)
                {
                    pluginsFound++;
                    ev.AddEventHandler(p, handler);
                }
            }
            return pluginsFound;
        }
        #endregion
    }
}